
use rocksdb::{DB, IteratorMode, Options};
use std::sync::Arc;

pub trait KVStore {

    fn init(file_path: &str) -> Self;

    fn save(&self, k: &str, v: &str) -> bool;

    fn find(&self, k: &str) -> Option<String>;

    fn delete(&self, k: &str) -> bool;

    fn iterator(&self) -> Vec<serde_json::Value>;

}
#[derive(Clone)]
pub struct RocksDB {
    db: Arc<DB>,
}

impl KVStore for RocksDB {

    fn init(file_path: &str) -> Self {
        let mut db_result = DB::open_default(file_path);
        if db_result.is_err() {
            let opts = Options::default();
            let repair_result = DB::repair(&opts, file_path);
            println!("{:?}", repair_result);
            db_result = DB::open_default(file_path);
        }
        RocksDB { db: Arc::new(db_result.unwrap()) }
    }

    fn save(&self, k: &str, v: &str) -> bool {
        self.db.put(k, v).is_ok()
    }

    fn find(&self, k: &str) -> Option<String> {
        match self.db.get(k.as_bytes()) {
            Ok(Some(v)) => {
                let result = String::from_utf8(v).unwrap();
                println!("Finding '{}' returns '{}'", k, result);
                Some(result)
            },
            Ok(None) => {
                println!("Finding '{}' returns None", k);
                None
            },
            Err(e) => {
                println!("Error retrieving value for {}: {}", k, e);
                None
            }
        }
    }

    fn delete(&self, k: &str) -> bool {
        self.db.delete(k.as_bytes()).is_ok()
    }
    
    fn iterator(&self) -> Vec<serde_json::Value> {
        
        let iterator = self.db.iterator(IteratorMode::End);

        // let taked = iterator.take(10).collect::<Vec<_>>();
        let taked = iterator
                .take(10)
                .map(|(_, v)| {
                    let value: serde_json::Value = serde_json::from_str(std::str::from_utf8(&v).unwrap()).unwrap();
                    return value;
                    // (std::str::from_utf8(&k).unwrap().to_string(), std::str::from_utf8(&v).unwrap().to_string())
                })
                .collect::<Vec<_>>();

        return taked;
    }
}